#include "webgraph.h"
#include "datatunnel.h"

#include <QWebEnginePage>
#include <QOpenGLWidget>
#include <QPointer>
#include <QEvent>
#include <QMouseEvent>
#include <QFile>
#include <QDrag>
#include <QMimeData>

class WebGraphPrivate
{
public:
    typedef struct
    {
        QString type;
        QString name;
        QList<double> data;
    } ItemData;

    void addItem(const QString& type, const QString& name, const QList<double>& data);

    QList<ItemData> items;
    QPointer<QOpenGLWidget> child;
    QWebEnginePage* page = nullptr;
    QString title;
    QColor titleColor = Qt::black;
};

void WebGraphPrivate::addItem(const QString &type, const QString& name, const QList<double> &data)
{
    items.append(ItemData{type, name, data});
}

WebGraph::WebGraph(QWidget* parent) : QWebEngineView(parent)
{
    d_ptr = new WebGraphPrivate;
    setWindowTitle(tr("Web Graph"));
    setAcceptDrops(true);
}

WebGraph::~WebGraph()
{
    delete d_ptr;
}

void WebGraph::setTitle(const QString& title)
{
    if (d_ptr->title != title) {
        d_ptr->title = title;

        // rerender ?
    }
}

QString WebGraph::title() const
{
    return d_ptr->title;
}

void WebGraph::setTitleColor(const QColor& color)
{
    if (d_ptr->titleColor != color) {
        d_ptr->titleColor = color;

        // rerender ?
    }
}

QColor WebGraph::titleColor() const
{
    return d_ptr->titleColor;
}

void WebGraph::addItem(const QString& type, const QString& name, const QList<double>& data)
{
    d_ptr->addItem(type, name, data);
}

int WebGraph::count() const
{
    return d_ptr->items.size();
}

void WebGraph::clear()
{
    d_ptr->items.clear();
}

void WebGraph::render()
{
    if (!d_ptr->page)
        d_ptr->page = new QWebEnginePage(this);

    QString html = "<script src=\"http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.js\"></script>"
                   "<script src=\"http://cdn.bootcss.com/highcharts/4.2.3/highcharts.js\"></script>"
                   "<div id=\"container\" style=\"min-width: 310px; height: 400px; margin: 0 auto\"></div>";

    QFile jsChannelFile(":./qwebchannel.js");
    if (!jsChannelFile.open(QIODevice::ReadOnly))
        qDebug() << "failed to read web channel file.";

    QString jswebchannel = "<script>" + jsChannelFile.readAll() + "</script>";
    QString commScript = "<script>window.onload = function() {"
                         "  console.log('script loaded');"
                         "if (location.search != \"\")"
                         "    var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]);"
                         "else"
                         "    var baseUrl = \"ws://localhost:1591\";"
                         "var socket = new WebSocket(baseUrl);"
                         "socket.onclose = function()"
                         "{"
                         "    console.error(\"web channel closed\");"
                         "};"
                         "socket.onerror = function(error)"
                         "{"
                         "    console.error(\"web channel error: \" + error);"
                         "};"
                         "socket.onopen = function()"
                         "{"
                         "    new QWebChannel(socket, function(channel) {"
                         "        window.dataTunnel = channel.objects.dataTunnel;"
                         "});"
                         "}}</script>";

    QString series;
    foreach (auto item, d_ptr->items) {
        QStringList strData;
        foreach (double data, item.data)
            strData.append(QString::number(data));
        series += QString("{events: { click: function (e) { window.dataTunnel.startDrag(); window.dataTunnel.setName(this.name); window.dataTunnel.setType(this.type);for(var i=0;i<this.data.length;i++){window.dataTunnel.addData(this.data[i].y);}}},type: '%1', name: '%2', data:[%3]},").arg(item.type).arg(item.name).arg(strData.join(','));
    }

    if (series.endsWith(','))
        series = series.remove(series.length() - 1);

    QString script;
    script = QString("<script>$(function() {$('#container').highcharts({title: { text:'%1', style: {color: '%2'} },series: [" + series + "]});});</script>").
            arg(d_ptr->title).arg(d_ptr->titleColor.name(QColor::HexRgb));

    html += script;
    html += jswebchannel;
    html += commScript;

    d_ptr->page->setHtml(html);
    setPage(d_ptr->page);
}

void WebGraph::mousePressEvent(QMouseEvent *e)
{
    DataTunnel::instance()->attach(this);
    if (!DataTunnel::instance()->isEmpty()) {
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
        mimeData->setData("data/graph", "graph");
        drag->setMimeData(mimeData);
        drag->exec();
    }

    QWebEngineView::mousePressEvent(e);
}

void WebGraph::mouseReleaseEvent(QMouseEvent *e)
{
    DataTunnel* dt = DataTunnel::instance();
    if (dt->attachedWidget() == this && this->rect().contains(e->pos()))
        dt->reset();

    QWebEngineView::mouseReleaseEvent(e);
}

void WebGraph::dragEnterEvent(QDragEnterEvent* e)
{
    const QMimeData* mimeData = e->mimeData();
    if (e->source() != this && mimeData->hasFormat("data/graph"))
        e->acceptProposedAction();
}

void WebGraph::dragMoveEvent(QDragMoveEvent* e)
{
    if (e->mimeData()->hasFormat(QStringLiteral("data/graph")))
        e->acceptProposedAction();
}

void WebGraph::dropEvent(QDropEvent *event)
{
    const QMimeData* mimeData = event->mimeData();

    if (mimeData->hasFormat("data/graph")) {
        DataTunnel* dt = DataTunnel::instance();
        QWidget* aw = dt->attachedWidget();
        if (!dt->isEmpty() && aw != nullptr && aw != this) {

            // now attach stored item to this graph
            addItem(dt->type(), dt->name(), dt->data());
            render();
            dt->reset();
        }
    }
}

bool WebGraph::event(QEvent *e)
{
    if (e->type() == QEvent::ChildAdded) {
        QChildEvent *child_ev = static_cast<QChildEvent*>(e);

        // there is also QObject child that should be ignored here;
        // use only QOpenGLWidget child
        QOpenGLWidget *w = qobject_cast<QOpenGLWidget*>(child_ev->child());
        if (w) {
            d_ptr->child = w;
            w->installEventFilter(this);
        }
    }
    return QWebEngineView::event(e);
}

bool WebGraph::eventFilter(QObject *obj, QEvent *e)
{
    // emit delegatePaint on paint event of the last added QOpenGLWidget child
    if (obj == d_ptr->child) {
        if (e->type() == QEvent::MouseButtonPress) {
            mousePressEvent((QMouseEvent*)e);
        } else if (e->type() == QEvent::MouseButtonRelease) {
            mouseReleaseEvent((QMouseEvent*)e);
        }
    }

    return QWebEngineView::eventFilter(obj, e);
}

void WebGraph::onMouseEvent(QMouseEvent *me)
{

}
